from pytest import raises

from readthedocs.config.exceptions import ConfigValidationError
from readthedocs.config.validation import (
    validate_bool,
    validate_choice,
    validate_list,
    validate_path,
    validate_string,
)


class TestValidateBool:
    def test_it_accepts_true(self):
        assert validate_bool(True) is True

    def test_it_accepts_false(self):
        assert validate_bool(False) is False

    def test_it_accepts_0(self):
        assert validate_bool(0) is False

    def test_it_accepts_1(self):
        assert validate_bool(1) is True

    def test_it_fails_on_string(self):
        with raises(ConfigValidationError) as excinfo:
            validate_bool("random string")
        assert excinfo.value.message_id == ConfigValidationError.INVALID_BOOL


class TestValidateChoice:
    def test_it_accepts_valid_choice(self):
        result = validate_choice("choice", ("choice", "another_choice"))
        assert result == "choice"

        with raises(ConfigValidationError) as excinfo:
            validate_choice("c", "abc")
        assert excinfo.value.message_id == ConfigValidationError.INVALID_LIST

    def test_it_rejects_invalid_choice(self):
        with raises(ConfigValidationError) as excinfo:
            validate_choice("not-a-choice", ("choice", "another_choice"))
        assert excinfo.value.message_id == ConfigValidationError.INVALID_CHOICE


class TestValidateList:
    def test_it_accepts_list_types(self):
        result = validate_list(["choice", "another_choice"])
        assert result == ["choice", "another_choice"]

        result = validate_list(("choice", "another_choice"))
        assert result == ["choice", "another_choice"]

        def iterator():
            yield "choice"

        result = validate_list(iterator())
        assert result == ["choice"]

        with raises(ConfigValidationError) as excinfo:
            validate_choice("c", "abc")
        assert excinfo.value.message_id == ConfigValidationError.INVALID_LIST

    def test_it_rejects_string_types(self):
        with raises(ConfigValidationError) as excinfo:
            validate_list("choice")
        assert excinfo.value.message_id == ConfigValidationError.INVALID_LIST


class TestValidatePath:
    def test_it_accepts_relative_path(self, tmpdir):
        tmpdir.mkdir("a directory")
        validate_path("a directory", str(tmpdir))

    def test_it_accepts_files(self, tmpdir):
        tmpdir.join("file").write("content")
        validate_path("file", str(tmpdir))

    def test_it_accepts_absolute_path(self, tmpdir):
        path = str(tmpdir.mkdir("a directory"))
        validate_path(path, "does not matter")

    def test_it_returns_relative_path(self, tmpdir):
        tmpdir.mkdir("a directory")
        path = validate_path("a directory", str(tmpdir))
        assert path == "a directory"

    def test_it_only_accepts_strings(self):
        with raises(ConfigValidationError) as excinfo:
            validate_path(None, "")
        assert excinfo.value.message_id == ConfigValidationError.INVALID_STRING


class TestValidateString:
    def test_it_accepts_unicode(self):
        result = validate_string("Unicöde")
        assert isinstance(result, str)

    def test_it_accepts_nonunicode(self):
        result = validate_string("Unicode")
        assert isinstance(result, str)

    def test_it_rejects_float(self):
        with raises(ConfigValidationError) as excinfo:
            validate_string(123.456)
        assert excinfo.value.message_id == ConfigValidationError.INVALID_STRING

    def test_it_rejects_none(self):
        with raises(ConfigValidationError) as excinfo:
            validate_string(None)
        assert excinfo.value.message_id == ConfigValidationError.INVALID_STRING
